import {Button, Col, Input, Popover, Row, Space, Tooltip, Tree, TreeDataNode, TreeProps} from "antd";
import React, {useEffect, useState} from "react";

import styles from './index.less';
import {DoubleLeftOutlined, MinusCircleOutlined} from "@ant-design/icons";


const {Search} = Input;


export type ColumnsMapProps = {
  id?: string;
  sourceSchema?: string;
  targetSchema?: string;
  initialValue?: API.ColumnPairDTO;
  value?: API.ColumnPairDTO;
  onChange?: (value: API.ColumnPairDTO) => void;
};


const lookup = (data: TreeDataNode[], key: React.Key,
                callback: (matchNode: TreeDataNode, matchIndex: number, sourceData: TreeDataNode[]) => void) => {
  for (let i = 0; i < data.length; i++) {
    if (data[i].key === key) {
      return callback(data[i], i, data);
    }
    if (data[i].children) {
      lookup(data[i].children!, key, callback);
    }
  }
};

const onRemove = (node: TreeDataNode,
                  data: TreeDataNode[],
                  updateFunc: (newData: TreeDataNode[], n: TreeDataNode) => void) => {
  const copyData = [...data];
  // find match index
  let matchIndex;
  lookup(copyData, node.key, (_, i) => matchIndex = i);
  copyData.splice(matchIndex!, 1);
  updateFunc(copyData, node);
}

const doDrop = (data: TreeDataNode[], dropKey: React.Key, dragKey: React.Key, dropPosition: number) => {

  // Find dragObject and  drop index
  let dragObj: TreeDataNode;
  lookup(data, dragKey, (item, index, arr) => {
    arr.splice(index, 1);
    dragObj = item;
  });
  let ar: TreeDataNode[] = [];
  let matchIndex: number;
  lookup(data, dropKey, (_item, index, arr) => {
    ar = arr;
    matchIndex = index;
  });
  if (dropPosition === -1) {
    // Drop on the top of the drop node
    ar.splice(matchIndex!, 0, dragObj!);
  } else {
    // Drop on the bottom of the drop node
    ar.splice(matchIndex! + 1, 0, dragObj!);
  }
  return ar;
};
const onChecked = (checkedKeys: React.Key[], treeData: TreeDataNode[], func: (keys: React.Key[], checkTreeData: TreeDataNode[]) => void) => {
  const checkedTreeData = treeData.filter(n => (checkedKeys.indexOf(n.key) > -1))
  func(checkedTreeData.map(i => i.key), checkedTreeData);
}

const searchLookup = (data: TreeDataNode[], searchValue: string): TreeDataNode[] => {
  if (!searchValue) {
    return data;
  }
  return data.filter((item) => {
    const strTitle = item.title as string;
    return strTitle.indexOf(searchValue) > -1;
  });
}


const ColumnsMap: React.FC<ColumnsMapProps> = (props) => {

  const [sourceSearchValue, setSourceSearchValue] = useState('');
  const [sourceCheckedKeys, setSourceCheckedKeys] = useState<React.Key[]>([]);
  const [sourceHiddenCheckedKeys, setSourceHiddenCheckedKeys] = useState<React.Key[]>([]);
  const [sourceTreeData, setSourceTreeData] = useState<TreeDataNode[]>([]);
  const [sourceCheckedData, setSourceCheckedData] = useState<TreeDataNode[]>([]);

  const [targetSearchValue, setTargetSearchValue] = useState('');
  const [targetCheckedKeys, setTargetCheckedKeys] = useState<React.Key[]>([]);
  const [targetHiddenCheckedKeys, setTargetHiddenCheckedKeys] = useState<React.Key[]>([]);
  const [targetTreeData, setTargetTreeData] = useState<TreeDataNode[]>([]);
  const [targetCheckedData, setTargetCheckedData] = useState<TreeDataNode[]>([]);


  // 初始化参数
  useEffect(() => {
    const underSourceColumns = props?.initialValue?.underSourceColumns;
    const underTargetColumns = props?.initialValue?.underTargetColumns;
    if (underSourceColumns && underTargetColumns) {
      const sourceColumns = underSourceColumns.map(k => ({title: k, key: k})) as TreeDataNode[];
      const targetColumns = underTargetColumns.map(k => ({title: k, key: k})) as TreeDataNode[];
      setSourceCheckedKeys(sourceColumns.map(i => i.key));
      setSourceCheckedData(sourceColumns);
      setTargetCheckedKeys(targetColumns.map(i => i.key));
      setTargetCheckedData(targetColumns);
    }
    const sourceColumn = props?.initialValue?.sourceColumns;
    const targetColumn = props?.initialValue?.targetColumns;
    if (sourceColumn && targetColumn) {
      const sourceTreeColumn = sourceColumn.map(k => ({title: k, key: k})) as TreeDataNode[];
      const targetTreeColumn = targetColumn.map(k => ({title: k, key: k})) as TreeDataNode[];
      setSourceTreeData(sourceTreeColumn);
      setTargetTreeData(targetTreeColumn);
    }
  }, [props?.initialValue]);

  const triggerOnChange = () => {
    const sourceColumns: string[] = sourceCheckedData.map(i => i.key as string);
    const targetColumns: string[] = targetCheckedData.map(i => i.key as string);
    props?.onChange?.({...props?.value, sourceColumns: sourceColumns, targetColumns: targetColumns})
  }

  // useState非实时更新，使用useEffect监听实现Form的onchange变更
  useEffect(() => {
    triggerOnChange();
  }, [sourceCheckedData, targetCheckedData]);

// 源搜索
  const onSourceChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const {value} = e.target;
    setSourceSearchValue(value);
  };

  const sourceTree: TreeDataNode[] = React.useMemo<TreeDataNode[]>(() => {
    if (sourceSearchValue) {
      setSourceHiddenCheckedKeys(sourceCheckedKeys);
    } else {
      setSourceCheckedKeys([...sourceHiddenCheckedKeys, ...sourceCheckedKeys]);
      setSourceHiddenCheckedKeys([]);
    }
    return searchLookup(sourceTreeData, sourceSearchValue);
  }, [sourceSearchValue, sourceTreeData]);

  // 目标搜索
  const onTargetChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const {value} = e.target;
    setTargetSearchValue(value);
  };

  const targetTree: TreeDataNode[] = React.useMemo<TreeDataNode[]>(() => {
    if (targetSearchValue) {
      setTargetHiddenCheckedKeys(targetCheckedKeys);
    } else {
      setTargetCheckedKeys([...targetHiddenCheckedKeys, ...targetCheckedKeys]);
      setTargetHiddenCheckedKeys([]);
    }
    return searchLookup(targetTreeData, targetSearchValue);
  }, [targetSearchValue, targetTreeData]);

  // 源映射字段拖拽
  const onSourceDrop: TreeProps['onDrop'] = (info) => {
    const dropKey = info.node.key;
    const dragKey = info.dragNode.key;
    const dropPos = info.node.pos.split('-');
    const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]); // the drop position relative to the drop node, inside 0, top -1, bottom 1
    const data = [...sourceCheckedData];
    const dragTree = doDrop(data, dropKey, dragKey, dropPosition);
    setSourceCheckedData(dragTree);
  };


  const onSourceChecked: TreeProps['onCheck'] = (checkedKeys) => {

    onChecked(checkedKeys as React.Key[], sourceTreeData, (keys, checkTreeData) => {
      const hiddenTreeData = sourceTreeData.filter(n => (sourceHiddenCheckedKeys.indexOf(n.key) > -1));
      setSourceCheckedKeys(keys);
      setSourceCheckedData([...hiddenTreeData, ...checkTreeData]);
    });
  }


  const onSourceAllAdd = () => {
    setSourceCheckedData(sourceTreeData);
    setSourceCheckedKeys(sourceTreeData.map(i => i.key));
  };

  const onSourceALLRemove = () => {
    setSourceCheckedData([]);
    setSourceCheckedKeys([]);
  };

  // 源选中字段移除
  const onCheckedSourceClick = (node: TreeDataNode) => {
    console.log(sourceCheckedData);
    onRemove(node, sourceCheckedData, (nd, n) => {
      setSourceCheckedData(nd);
      const k = [...sourceCheckedKeys];
      for (let i = 0; i < k.length; i++) {
        let key = k[i];
        if (key === n.key) {
          k.splice(i, 1);
          setSourceCheckedKeys(k);
          break;
        }
      }
      const hiddenCheckedKeys = [...sourceHiddenCheckedKeys];
      for (let i = 0; i < hiddenCheckedKeys.length; i++) {
        let key = hiddenCheckedKeys[i];
        if (key === n.key) {
          hiddenCheckedKeys.splice(i, 1);
          setSourceHiddenCheckedKeys(hiddenCheckedKeys);
          break;
        }
      }
    })
  }
  // 目标映射字段移动
  const onTargetDrop: TreeProps['onDrop'] = (info) => {
    const dropKey = info.node.key;
    const dragKey = info.dragNode.key;
    const dropPos = info.node.pos.split('-');
    const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]); // the drop position relative to the drop node, inside 0, top -1, bottom 1
    const data = [...targetCheckedData];
    const dragTree = doDrop(data, dropKey, dragKey, dropPosition);
    setTargetCheckedData(dragTree);
  };

  const onTargetChecked: TreeProps['onCheck'] = (checkedKeys) => {
    onChecked(checkedKeys as React.Key[], targetTreeData, (keys, checkTreeData) => {
      const hiddenTreeData = targetTreeData.filter(n => (targetHiddenCheckedKeys.indexOf(n.key) > -1));
      setTargetCheckedKeys(keys);
      setTargetCheckedData([...checkTreeData, ...hiddenTreeData]);
    });
  }


  // 目标选中字段移除
  const onCheckedTargetClick = (node: TreeDataNode) => {
    onRemove(node, targetCheckedData, (nd, n) => {
      setTargetCheckedData(nd);
      const k = [...targetCheckedKeys];
      for (let i = 0; i < k.length; i++) {
        let key = k[i];
        if (key === n.key) {
          k.splice(i, 1);
          setTargetCheckedKeys(k);
          break;
        }
      }

      const hiddenCheckedKeys = [...targetHiddenCheckedKeys];
      for (let i = 0; i < hiddenCheckedKeys.length; i++) {
        let key = hiddenCheckedKeys[i];
        if (key === n.key) {
          hiddenCheckedKeys.splice(i, 1);
          setTargetHiddenCheckedKeys(hiddenCheckedKeys);
          break;
        }
      }
    })
  }


  const onTargetAllAdd = () => {
    setTargetCheckedData(targetTreeData);
    setTargetCheckedKeys(targetTreeData.map(i => i.key));
  };

  const onTargetALLRemove = () => {
    setTargetCheckedData([]);
    setTargetCheckedKeys([]);
  };


  return (<>
    <Row gutter={[48, 24]} style={{width: 500}} justify={'center'}>
      <Col span={12}>
        <div>
          <Popover content={props?.sourceSchema ? `${props.sourceSchema}` : ''}>
            <span className={styles.labelTree}>源字段</span>
          </Popover>
          <Search
            allowClear
            variant={'filled'}
            onChange={onSourceChange}
            placeholder="Search"/>
          <div className={styles.treeWrapContainer}>
            <Tree
              blockNode={true}
              checkable={true}
              selectable={false}
              autoExpandParent={false}
              defaultExpandParent={false}
              checkedKeys={sourceCheckedKeys}
              onCheck={onSourceChecked}
              treeData={sourceTree}
              height={290}
              className={styles.treeData}
            />
          </div>
        </div>
      </Col>
      <Col span={12}>
        <div>
          <Popover content={props?.targetSchema ? `${props.targetSchema}` : ''}>
            <span className={styles.labelTree}>目标字段</span>
          </Popover>
          <Search
            variant={'filled'}
            allowClear
            onChange={onTargetChange}
            placeholder="Search"/>
          <div className={styles.treeWrapContainer}>
            <Tree
              checkable={true}
              blockNode={true}
              selectable={false}
              autoExpandParent={false}
              defaultExpandParent={false}
              checkedKeys={targetCheckedKeys}
              onCheck={onTargetChecked}
              treeData={targetTree}
              height={290}
              className={styles.treeData}
            />
          </div>
        </div>
      </Col>
      <Col span={12} style={{textAlign: 'center'}}>
        <Space>
          <Tooltip title={'添加'}>
            <Button type={'default'} onClick={onSourceAllAdd}><DoubleLeftOutlined
              style={{transform: 'rotate(-90deg)'}}/></Button>
          </Tooltip>
          <Tooltip title={'移除'} placement={'top'}>
            <Button type={'default'} onClick={onSourceALLRemove}><DoubleLeftOutlined
              style={{transform: 'rotate(90deg)'}}/></Button>
          </Tooltip>
        </Space>
      </Col>
      <Col span={12} style={{textAlign: 'center'}}>
        <Space>
          <Tooltip title={'添加'} placement={'top'}>
            <Button type={'default'} onClick={onTargetAllAdd}><DoubleLeftOutlined
              style={{transform: 'rotate(-90deg)'}}/></Button>
          </Tooltip>
          <Tooltip title={'移除'} placement={'top'}>
            <Button type={'default'} onClick={onTargetALLRemove}><DoubleLeftOutlined
              style={{transform: 'rotate(90deg)'}}/></Button>
          </Tooltip>
        </Space>
      </Col>
      {/* source column mapping */}
      <Col span={12}>
        <div className={styles.wrapContainer}>
          <Tree
            height={290}
            treeData={sourceCheckedData}
            onDrop={onSourceDrop}
            draggable={true}
            blockNode={true}
            titleRender={(node) => {
              const {title} = node;
              return (
                <div className="treeItem">
                  <div className="name">
                    <Space>
                      <span>{`${title}`}</span>
                      <a
                        onClick={() => onCheckedSourceClick(node)}>
                        <MinusCircleOutlined/>
                      </a>
                    </Space>
                  </div>
                </div>);
            }}
          />
        </div>
      </Col>
      {/* target column mapping */}
      <Col span={12}>
        <div className={styles.wrapContainer}>
          <Tree
            blockNode={true}
            height={290}
            treeData={targetCheckedData}
            onDrop={onTargetDrop}
            draggable={true}
            titleRender={(node) => {
              const {title} = node;
              return (
                <div className="treeItem">
                  <div className="name">
                    <Space>
                      <span>{`${title}`}</span>
                      <a
                        onClick={() => onCheckedTargetClick(node)}>
                        <MinusCircleOutlined/>
                      </a>
                    </Space>
                  </div>
                </div>);
            }}
          />
        </div>
      </Col>
    </Row>
  </>);
}

export default ColumnsMap;
